import os
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder, StandardScaler
from sklearn.metrics import classification_report, roc_auc_score, confusion_matrix, ConfusionMatrixDisplay

import joblib
import matplotlib.pyplot as plt
import seaborn as sns
from xgboost import XGBClassifier

os.chdir(r"D:\Workspace\PythonProject\python\telentLoss")
df = pd.read_csv(r"./data/raw/train.csv")
# df.info()
print(df.head(10).to_string())
# print(df['Department'].unique())
# ['Research & Development' 'Sales' 'Human Resources'] 对部门去重发现只有三个部门 研发,销售和人力资源
# print(df['EducationField'].drop_duplicates())
# print(df['EducationField'].unique())
# ['Life Sciences' 'Medical' 'Other' 'Technical Degree' 'Human Resources'
#  'Marketing'] 对教育领域的去重发现 教育背景是 生命科学,医学,其他,技术学位, 人力资源,市场营销
# 对年龄特征进行绘图 对收入进行绘图 年龄和收入之间的关系进行绘图
age_counts = df['Age'].value_counts()  # 年龄构成(正态分布)

# 对数据处理发现
attr = df['Attrition'].value_counts()
print(attr)  # 0:992,1:178 那么也就是1代表流失人才,0 代表没有流失还在公司
# 对工作距离,环境满意度,工作投入状况,工作满意度,总工作年限,工作与生活平衡度进行去重看看
distance = df[
    'DistanceFromHome'].unique()  # [ 1, 7  4  9  2 22 10  3  8 15 19 14 18 17 23 24  5 29 26  6 27 16 13 21 25 28 12 11 20]
envir = df['EnvironmentSatisfaction'].unique()
envir_cnt = df['EnvironmentSatisfaction'].value_counts()
job_saf = df['JobSatisfaction'].unique()  # [1,2,3,4]
print(df['JobSatisfaction'].value_counts())
# 4    350
# 3    325
# 1    219
# 2    206
# print(envir_cnt)
# print(envir) # [[1 4 2 3]][4:338,3:337,1:215:2:210]
# print(df['JobRole'].unique())
# print(df['EducationField'].unique())
# print(df['Department'].unique())
# 绘图
plt.figure(figsize=(8, 6))
sns.boxplot(x='Attrition', y='MonthlyIncome', data=df, palette='Set2')
plt.title('Monthly Income vs Attrition')
plt.xlabel('Attrition (0: Stayed, 1: Left)')
plt.ylabel('Monthly Income')
plt.xticks([0, 1], ['Stayed', 'Left'])
plt.grid(axis='y', linestyle='--', alpha=0.7)
plt.tight_layout()
plt.savefig('./data/processed/fig/monthly_income_vs_attrition_boxplot.png', dpi=300)
# plt.show()
# 计算离职与未离职员工的平均月收入
avg_income = df.groupby('Attrition')['MonthlyIncome'].mean().reset_index()
avg_income.columns = ['Attrition', 'avg_monthly_income']
# print(avg_income)
plt.figure(figsize=(8, 6))
sns.barplot(x='Attrition', y='avg_monthly_income', data=avg_income, palette='Blues_d')
plt.title('Average Monthly Income by Attrition')
plt.xlabel('Attrition (0: Stayed, 1: Left)')
plt.ylabel('Average Monthly Income')
plt.xticks([0, 1], ['Stayed', 'Left'])
plt.grid(axis='y', linestyle='--', alpha=0.7)
plt.tight_layout()
plt.savefig('./data/processed/fig/avg_monthly_income_by_attrition.png', dpi=300)
# plt.show()
# 对年龄和收入进行画图看看之间的关系
plt.figure(figsize=(20, 10))
plt.bar(df['Age'], df['MonthlyIncome'], color='b', alpha=0.7)
plt.xlabel('Age')
plt.ylabel('MonthlyIncome')
plt.title('Age vs MonthlyIncome')
plt.grid()
# plt.show()
# 保存图像
plot_path = os.path.join('./data/processed/fig', "age_vs_monthly_income.png")
plt.savefig(plot_path, dpi=300, bbox_inches='tight')
# 看看股票期权的人的离职率
stock_option = df['StockOptionLevel'].value_counts()
# print(stock_option)
"""
StockOptionLevel:
0    473
1    446
2    122
3     59
"""
# 按 StockOptionLevel 分组并计算每组的离职率
grouped = df.groupby('StockOptionLevel')['Attrition'].mean().reset_index()
# 重命名列用于显示
grouped.columns = ['StockOptionLevel', 'AttritionRate']
# print(grouped)
# 筛选 StockOptionLevel 等于 3 并且 Attrition 等于 1 的行，然后统计数量
level_3_attrited = df[(df['StockOptionLevel'] == 3) & (df['Attrition'] == 1)].shape[0]
# print(level_3_attrited)

# 绘图
plt.figure(figsize=(8, 6))
sns.barplot(x='StockOptionLevel', y='AttritionRate', data=grouped, palette='Greens_d')
plt.title('Attrition Rate by Stock Option Level')
plt.xlabel('Stock Option Level (0: No Option)')
plt.ylabel('Attrition Rate')
plt.ylim(0, 1)
plt.grid(axis='y', linestyle='--', alpha=0.7)
plt.tight_layout()
plt.savefig('./data/processed/fig/attrition_by_stockoption.png', dpi=300)
# plt.show()
# 绘图看看工作满意度和离职之间的关系
plt.figure(figsize=(8, 6))
sns.boxplot(x='Attrition', y='JobSatisfaction', data=df, palette='Set2')
plt.title('Job Satisfaction by Attrition')
plt.xlabel('Attrition (0: No, 1: Yes)')
plt.ylabel('Job Satisfaction')
plt.xticks([0, 1], ['Stayed', 'Left'])
plt.yticks([1, 2, 3, 4])
plt.grid(axis='y', linestyle='--', alpha=0.7)
plt.tight_layout()
plt.savefig('./data/processed/fig/boxplot_job_satisfaction_by_attrition.png', dpi=300)
# plt.show()

# 看看加班与离职之间的关系
# 看看PercentSalaryHike涨薪幅度跟离职的之间的关系

# 看看出差与离职之间的关系
# 看看YearsAtCompany  工作年限与工资的水平 我的理解是跳槽是涨薪最快的方式
# 看看PercentSalaryHike 涨薪幅度最大是多少
max_salary_hike = df['PercentSalaryHike'].max()
# print(f"PercentSalaryHike 最大值是：{max_salary_hike}") # 输出结果 PercentSalaryHike 最大值是：25
# avg_income_by_years = df.groupby('YearsAtCompany')['MonthlyIncome'].max()
# # 按工作年限分组并计算平均月收入
# avg_income_by_years = df.groupby('YearsAtCompany')['MonthlyIncome'].mean().reset_index()
#
# plt.figure(figsize=(10, 6))
# sns.lineplot(x='YearsAtCompany', y='MonthlyIncome', data=avg_income_by_years, marker='o', color='b')
# plt.title('Max Monthly Income by Years at Company')
# plt.xlabel('Years at Company')
# plt.ylabel('Max Monthly Income')
# plt.grid(True, linestyle='--', alpha=0.5)
# plt.tight_layout()
# plt.savefig('./data/processed/fig/avg_income_by_years_at_company.png', dpi=300)
# plt.show()
# print(avg_income_by_years)
# 看看 RelationshipSatisfaction 人际关系与离职之间的关系
plt.figure(figsize=(8, 6))
sns.boxplot(x='Attrition', y='RelationshipSatisfaction', data=df, palette='Set2')
plt.title('Relationship Satisfaction by Attrition')
plt.xlabel('Attrition (0: Stayed, 1: Left)')
plt.ylabel('Relationship Satisfaction')
plt.xticks([0, 1], ['Stayed', 'Left'])
plt.yticks([1, 2, 3, 4])  # 满意度为4个等级
plt.grid(axis='y', linestyle='--', alpha=0.7)
plt.tight_layout()
plt.savefig('./data/processed/fig/relationship_satisfaction_by_attrition.png', dpi=300)
# plt.show()
# 看看加班与离职之间的关系
# 按 OverTime 分组并计算离职率
grouped = df.groupby('OverTime')['Attrition'].mean().reset_index()
grouped.columns = ['OverTime', 'AttritionRate']
print(grouped)

plt.figure(figsize=(8, 6))
sns.barplot(x='OverTime', y='AttritionRate', data=grouped, palette='Blues_d')
plt.title('Attrition Rate by OverTime')
plt.xlabel('OverTime (0: No, 1: Yes)')
plt.ylabel('Attrition Rate')
plt.ylim(0, 1)
plt.grid(axis='y', linestyle='--', alpha=0.7)
plt.tight_layout()
plt.savefig('./data/processed/fig/attrition_rate_by_overtime.png', dpi=300)
# plt.show()
# 创建交叉表
contingency_table = pd.crosstab(df['OverTime'], df['Attrition'])

# 绘制热力图
plt.figure(figsize=(6, 4))
sns.heatmap(contingency_table, annot=True, fmt='d', cmap='YlGnBu', cbar=False)
plt.title('OverTime vs Attrition (Count)')
plt.xlabel('Attrition (0: Stayed, 1: Left)')
plt.ylabel('OverTime (0: No, 1: Yes)')
plt.tight_layout()
plt.savefig('./data/processed/fig/overtime_vs_attrition_heatmap.png', dpi=300)
# plt.show()

# 特征选取
x = df[
    ['BusinessTravel',  'EnvironmentSatisfaction',  'JobSatisfaction', 'JobLevel',
     'MonthlyIncome', 'OverTime', 'PercentSalaryHike', 'PerformanceRating',
     'StockOptionLevel', 'WorkLifeBalance',"DistanceFromHome",'RelationshipSatisfaction','YearsSinceLastPromotion']]
y = df['Attrition']
# 对BusinessTravel进行labelEncoder
le=LabelEncoder()
x['BusinessTravel']=le.fit_transform(x['BusinessTravel'])
# 对overtime进行labelEncoder
x['OverTime']=le.fit_transform(x['OverTime'])
# print(x.to_string())
# 数据预处理
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42, stratify=y)

# 特征缩放（可选，XGBoost 不强制要求）
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# 构建并训练 XGBoost 模型
model = XGBClassifier(
    max_depth=7,
    learning_rate=0.01,
    n_estimators=100,
    random_state=42
)

model.fit(X_train_scaled, y_train)

# 预测与评估
y_pred = model.predict(X_test)
y_pred_proba = model.predict_proba(X_test)[:, 1]

# 输出评估指标
print("分类报告：")
print(classification_report(y_test, y_pred))

print("ROC AUC Score:", roc_auc_score(y_test, y_pred_proba))

# 可视化混淆矩阵
cm = confusion_matrix(y_test, y_pred)
disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=['Stayed', 'Left'])
disp.plot(cmap='Blues')
plt.title('Confusion Matrix')
plt.savefig('./data/processed/fig/confusion_matrix_xgboost.png', dpi=300, bbox_inches='tight')
plt.show()

# 保存模型
joblib.dump(model, './models/xgboost_attrition_model.pkl')




